package com.jacklin.snowy.limitstrategy.service.impl;

import com.jacklin.snowy.limitstrategy.annotation.RequestLimiter;
import com.jacklin.snowy.limitstrategy.constants.RedisKeyConstant;
import com.jacklin.snowy.limitstrategy.dto.RequestLimitDTO;
import com.jacklin.snowy.limitstrategy.enums.RequestLimitType;
import com.jacklin.snowy.limitstrategy.service.RequestLimitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.support.atomic.RedisAtomicInteger;
import org.springframework.stereotype.Service;

import java.time.LocalTime;

/**
 * 固定窗口 限流
 *
 * @author: jacklin
 * @since: 2022/5/10 14:11
 */
@Slf4j
@Service
public class FixedWindowRateLimitServiceImpl implements RequestLimitService {

    @Autowired
    private RedisConnectionFactory factory;

    @Override
    public boolean checkRequestLimit(RequestLimitDTO dto) {
        String key = RedisKeyConstant.RequestLimit.QPS_FIXED_WINDOW + dto.getKey();
        RequestLimiter limiter = dto.getLimiter();
        RedisAtomicInteger atomicCount = new RedisAtomicInteger(key, factory);
        int count = atomicCount.getAndIncrement();
        if (count == 0) {
            atomicCount.expire(limiter.time(), limiter.unit());
        }
        log.info("限流配置：{} {} 内允许访问 {} 次", limiter.time(), limiter.unit(), limiter.limitCount());
        log.info("访问时间【{}】", LocalTime.now());
        // 检测是否到达限流值
        if (count >= limiter.limitCount()) {
            String msg = "【" + key + "】限流控制，" + limiter.time() + " " + limiter.unit().name() + "内只允许访问 " + limiter.limitCount() + " 次";
            log.info(msg);
            return true;
        } else {
            log.info("未达到限流值，放行 {}/{}", count, limiter.limitCount());
            return false;
        }
    }

    @Override
    public RequestLimitType getType() {
        return RequestLimitType.FIXED_WINDOW;
    }
}
